package com.zhijian.warehouse.manage.util.access;

import com.gg.reader.api.dal.GClient;
import com.gg.reader.api.dal.HandlerTagEpcLog;
import com.gg.reader.api.dal.HandlerTcpDisconnected;
import com.gg.reader.api.protocol.gx.LogBaseEpcInfo;
import com.zhijian.util.BeanUtil;
import com.zhijian.warehouse.manage.config.access.AccessConfig;
import com.zhijian.warehouse.manage.entity.vo.response.AccessAioAndroidBingVo;
import com.zhijian.warehouse.manage.service.AccessOptService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class AccessAioUtil {
    private final static Logger logger = LoggerFactory.getLogger(AccessAioUtil.class);
    private static final byte RT_CODE = 0x00;
    private static final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
    private final AccessConfig accessConfig;
    private final int delay = 60;
    private final String aioName = "一体机";
    private final AccessOptService accessOptService;
    private final AccessAioAndroidBingVo accessAioAndroidBingVo;

    public AccessAioUtil(GClient aioClient, AccessConfig accessConfig,
                         AccessOptService accessOptService,
                         AccessAioAndroidBingVo accessAioAndroidBingVo
    ) {
        this.accessConfig = accessConfig;
        this.accessOptService = accessOptService;
        this.accessAioAndroidBingVo = accessAioAndroidBingVo;
        if (accessConfig.getAio().getEnable())
            reconnectWithRetry(aioClient, aioName, this.accessAioAndroidBingVo.getIpAddr(), accessConfig.getAio().getTimeout(), new AtomicInteger(0));
    }

    public synchronized static void initAioAccess(GClient aioClient, AccessConfig accessConfig,
                                                  AccessOptService accessFacilityRecordService
            , AccessAioAndroidBingVo accessAioAndroidBingVo
    ) {
        new AccessAioUtil(aioClient, accessConfig, accessFacilityRecordService, accessAioAndroidBingVo);
    }

    private void subscribeHandler(GClient client, String ip4) {
        client.onTagEpcLog = new HandlerTagEpcLog() {
            @Override
            public void log(String s, LogBaseEpcInfo logBaseEpcInfo) {
                if (null != logBaseEpcInfo && logBaseEpcInfo.getResult() == 0) {
                    logger.info(String.format("%s ip:%s  aioLogBaseEpcInfo.epc:%s", aioName, accessAioAndroidBingVo.getIpAddr(), logBaseEpcInfo.getEpc()));
                    //发送感应消息
                    accessOptService.aioConnect(logBaseEpcInfo.getEpc(), accessAioAndroidBingVo.getAndroidIpAddr());
                }
            }
        };
    }

    //订阅TCP断开连接上报
    private void subscribeHandler(final GClient client, String name, String ipPort, int timeout) {
        client.onDisconnected = new HandlerTcpDisconnected() {
            @Override
            public void log(String s) {
                logger.info("{}{}已断开", name, s);
                client.setSendHeartBeat(false);
                client.close();//释放当前连接资源
                AtomicInteger retryCount = new AtomicInteger(0);
                reconnectWithRetry(client, name, ipPort, timeout, retryCount);
            }
        };
    }

    //重连接
    private void reconnectWithRetry(final GClient client, String name, String ipPort, int timeout, AtomicInteger retryCount) {
        Integer maxRetries = accessConfig.getAio().getMaxRetries();
        if (BeanUtil.isNotEmpty(accessAioAndroidBingVo)) //一体机读写器最大重试次数
            maxRetries = accessConfig.getAio().getMaxRetries();
        if (retryCount.get() >= maxRetries) {
            client.setSendHeartBeat(false);
            client.close();
            logger.error("{}{}连接失败，已达到最大重试次数", name, ipPort);
        } else {
            scheduledExecutorService.schedule(new Runnable() {
                @Override
                public void run() {
                    synchronized (client) { //控制同一个客户端重复连接断开争夺资源
                        logger.info("{}连接尝试中...{} 每{}秒重试一次 第{}次 将会在{}次后中断连接", name, ipPort, delay, retryCount.get(), accessConfig.getAio().getMaxEnable() ? accessConfig.getAio().getMaxRetries() : -1);
                        retry(client, name, ipPort, timeout, retryCount);
                    }
                }
            }, delay, TimeUnit.SECONDS); // 5000ms后重试
        }
    }

    private void retry(final GClient client, String name, String ipPort, int timeout, AtomicInteger retryCount) {
        // 尝试重新连接
        if (accessConfig.getAio().getMaxEnable())
            retryCount.getAndIncrement();
        boolean flag = initClient(client, name, ipPort, timeout);
        if (!flag) {
            reconnectWithRetry(client, name, ipPort, timeout, retryCount); // 递归调用
        } else {
            logger.info("{}重连成功，ip:{}", name, ipPort);
            retryCount.set(0);// 重置重试计数
        }
    }

    private boolean initClient(GClient client, String name, String ipPort, int timeout) {
        if (client.openTcp(ipPort, 0)) {
            //订阅TCP断开连接上报
            subscribeHandler(client, accessAioAndroidBingVo.getAndroidIpAddr());
            //设置发送心跳检测tcp连接是否正常  默认关闭
            subscribeHandler(client, name, ipPort, timeout);
            logger.info("---------{}监听开启,ip:{}, ---------", name, ipPort);
            return true;
        }
        return false;
    }
}
